package constantReader;

import java.io.DataInputStream;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.nio.charset.StandardCharsets;
import java.util.*;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;

public class JarConstantReader {
    private static final int CONSTANT_Utf8 = 1;
    private static final int CONSTANT_String = 8;

    public static void main(String[] args) throws IOException {
        Map<String, List<String>> stringListMap = readJar("*.jar");
        for(Map.Entry<String, List<String>> map : stringListMap.entrySet()) {
            System.out.println(map.getKey());
            System.out.println(map.getValue());
        }
    }

    public static Map<String, List<String>> readJar(String jarFilePath) throws IOException {
        Map<String, List<String>> clsMap = new HashMap<>();
        try (JarFile jarFile = new JarFile(jarFilePath)) {
            Enumeration<JarEntry> entries = jarFile.entries();
            while (entries.hasMoreElements()) {
                JarEntry entry = entries.nextElement();
                String entryName = entry.getName();
                if (entryName.endsWith(".class")) {
                    clsMap.put(entryName, read(jarFile.getInputStream(entry)));
                }
            }
        }
        return clsMap;
    }

    public static List<String> read(String clsFilePath){
        try {
            return read(new FileInputStream(clsFilePath));
        } catch (Exception e) {
            System.out.println(e.getMessage());
        }
        return new ArrayList<>();
    }

    public static List<String> read(InputStream inputStream) {
        try (DataInputStream dis = new DataInputStream(inputStream)) {
            int magic = dis.readInt(); // 魔数： CA FE BA BE
            short minorVersion = dis.readShort(); // 版本号
            short majorVersion = dis.readShort();
            short constantPoolCount = dis.readShort(); // 常量池大小
            String[] constantUTF8 = new String[constantPoolCount]; // 存储常量
            List<Integer> stringIndex = new ArrayList<>(); // 存储字符串索引

            // 开始解析常量池
            for (int i = 1; i < constantPoolCount; i++) {
                byte tag = dis.readByte();
                switch (tag) {
                    case CONSTANT_Utf8:
                        // 解析 UTF-8 字符串常量
                        int length = dis.readUnsignedShort();
                        byte[] bytes = new byte[length];
                        dis.readFully(bytes);
                        constantUTF8[i] = new String(bytes, StandardCharsets.UTF_8);
                        break;
                    case CONSTANT_String:
                        // 解析字符串常量
                        int idx = dis.readShort();
                        stringIndex.add(idx);
                        break;
                    default:
                        // 跳过其他类型的常量
                        i = skipConstant(dis, tag, i);
                        break;
                }
            }
            return stringIndex.stream().map(idx -> constantUTF8[idx]).toList();
        } catch (Exception e) {
            System.out.println(e.getMessage());
        }
        return new ArrayList<>();
    }

    private static int skipConstant(DataInputStream dis, byte tag, int i) throws IOException {
        switch (tag) {
            case 3: // CONSTANT_Integer
            case 4: // CONSTANT_Float
                dis.skipBytes(4);
                break;
            case 5: // CONSTANT_Long
            case 6: // CONSTANT_Double
                dis.skipBytes(8);
                i++;
                break;
            case 7: // CONSTANT_Class
            case 8: // CONSTANT_String
            case 16: // CONSTANT_MethodType
            case 19: // CONSTANT_Module
            case 20: // CONSTANT_Package
                dis.skipBytes(2);
                break;
            case 9: // CONSTANT_Fieldref
            case 10: // CONSTANT_Methodref
            case 11: // CONSTANT_InterfaceMethodref
            case 12: // CONSTANT_NameAndType
            case 18: // CONSTANT_InvokeDynamic
                dis.skipBytes(4);
                break;
            case 15: // CONSTANT_MethodHandle
                dis.skipBytes(3);
                break;
            case 21: // CONSTANT_UnresolvedClass
                dis.skipBytes(2);
                break;
        }
        return i;
    }
}
